package com.wsl.HealthChallenge.controllers;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.wsl.HealthChallenge.models.ContestEntry;
import com.wsl.HealthChallenge.models.ContestType;
import com.wsl.HealthChallenge.models.UserStats;
import com.wsl.HealthChallenge.utils.DataStorageUtils;
import com.wsl.HealthChallenge.utils.MapSortingUtils;
import com.wsl.HealthChallenge.utils.PMF;
import com.wsl.HealthChallenge.utils.Utils;

/**
 * Controller for the admin page of a contest.
 * 
 * Handles request to URLs of the type www.walk2.me/{challengeId}/admin
 * 
 * @author mark
 */
@Controller
public class ChallengeAdminController {
    SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
    @RequestMapping("/{challengeId}/admin")
    public ModelAndView handleRequest(@PathVariable String challengeId,
            @RequestParam(value="created", defaultValue="0") boolean challengeCreated,
            @RequestParam(value="updated", defaultValue="0") boolean challengeUpdated,
            @RequestParam(value="invalidDateFormat", defaultValue="0") boolean invalidDateFormat,
            HttpServletRequest request, HttpServletResponse response) throws Exception {
        ContestEntry challenge = ensureChallengeExists(challengeId, response);
        if (challenge == null) {
            return null;
        }

        if (!ensureUserIsSignedInAsAdmin(request, response, challenge)) {
            return null;
        }

        ModelMap model = new ModelMap();
        model.addAttribute("justCreated", challengeCreated);
        model.addAttribute("updated", challengeUpdated);
        model.addAttribute("invalidDateFormat", invalidDateFormat);
        model.addAttribute("challenge", challenge);
        model.addAttribute("startDate", dateFormat.format(challenge.getStartDate()));

        return new ModelAndView("admin_challenge_view", model);
    }
 
    @RequestMapping("/{challengeId}/admin/update")
    public ModelAndView handleUpdateRequest(@PathVariable String challengeId,
            @RequestParam(value="name", defaultValue="") String name,
            @RequestParam(value="contestType", defaultValue="") String contestTypeString,
            @RequestParam(value="description", defaultValue="") String description,
            @RequestParam(value="startDate", defaultValue="") String startDate,
            HttpServletRequest request, HttpServletResponse response) throws Exception {
        PersistenceManager pm = PMF.get().getPersistenceManager();
        pm.currentTransaction().begin();
        boolean invalidDateFormat = false;
        try {
            ContestEntry challenge = pm.getObjectById(ContestEntry.class, challengeId);

            if (!ensureUserIsSignedInAsAdmin(request, response, challenge)) {
                return null;
            }

            if (!name.isEmpty()) {
                challenge.setName(name);
                JDOHelper.makeDirty(challenge, "name");
            }

            if (!description.isEmpty()) {
                challenge.setDescription(description);
                JDOHelper.makeDirty(challenge, "description");
            }

            ContestType contestType = ContestType.getFromStringRepresentation(contestTypeString);
            challenge.setType(contestType);
            JDOHelper.makeDirty(challenge, "type");

            try {
                if (!startDate.isEmpty()) {
                    Date date = dateFormat.parse(startDate);
                    challenge.setStartDate(date);
                    JDOHelper.makeDirty(challenge, "startDate");
                }
            } catch(ParseException e){
                invalidDateFormat = false;
            }
            pm.currentTransaction().commit();
        //} catch (Exception e) {
        //    return false;
        } finally {
            if (pm.currentTransaction().isActive()) {
                pm.currentTransaction().rollback();
            }
        }
        StringBuilder builder = new StringBuilder("/");
        builder.append(challengeId);
        builder.append("/admin?updated=1");
        if (invalidDateFormat) {
            builder.append("&invalidDateFormat=1");
        }
        //response.getWriter().println("Challenge updated.");
        response.sendRedirect(builder.toString());
        return null;
    }


    @RequestMapping("/{challengeId}/admin/standings")
    public ModelAndView handleStandingsRequest(@PathVariable String challengeId,
            HttpServletRequest request, HttpServletResponse response) throws Exception {
        // TODO(artem): Remove code duplication.
        ContestEntry challenge = ensureChallengeExists(challengeId, response);
        if (challenge == null) {
            return null;
        }

        if (!ensureUserIsSignedInAsAdmin(request, response, challenge)) {
            return null;
        }

        ModelMap model = new ModelMap();
        Map<String, UserStats> userStats = challenge.getUserStats();

        Map<String, UserStats> sortedUserStats = 
            MapSortingUtils.sortMapByValue(userStats, MapSortingUtils.SortingOrder.DESCENDING);

        model.addAttribute("challengeName", challenge.getName());
        model.addAttribute("challengeType", challenge.getType());
        model.addAttribute("numParticipants", String.valueOf(userStats.size()));
        model.addAttribute("allUsers", sortedUserStats.entrySet());
        
        return new ModelAndView("admin_standings_view", model);
    }

    @RequestMapping("/{challengeId}/admin/delete")
    public ModelAndView handleDeleteRequest(@PathVariable String challengeId,
            HttpServletRequest request, HttpServletResponse response) throws Exception {
        // TODO(artem): Remove code duplication.
        ContestEntry challenge = ensureChallengeExists(challengeId, response);
        if (challenge == null) {
            return null;
        }

        if (!ensureUserIsSignedInAsAdmin(request, response, challenge)) {
            return null;
        }
        
        DataStorageUtils.deleteChallenge(challenge);
        response.getWriter().write("Deleted challenge " + challenge.getId());
        return null;
    }

    /**
     * Ensures the current user is signed in and is an admin for this challenge. Prints
     * simple error messages if either of these things is not true.
     */
    private boolean ensureUserIsSignedInAsAdmin(HttpServletRequest request,
            HttpServletResponse response, ContestEntry challenge) throws IOException {
        // If not logged in, do that first and come back to the same URL.
        if (!Utils.ensureUserLoggedIn(request, response)) {
            return false;
        }
        UserService userService = UserServiceFactory.getUserService();
        User user = userService.getCurrentUser();

        if (!challenge.isAdminUser(user.getUserId())) {
            response.getWriter().println("You are not an admin of this challenge.");
            return false;
        }
        
        return true;
    }

    /**
     * Ensures a challenge with the given id exists. Prints a simple error messages if it does 
     * not.
     */
    private ContestEntry ensureChallengeExists(String challengeId,
            HttpServletResponse response) throws IOException {
        ContestEntry challenge = DataStorageUtils.getContest(challengeId);
        if (challenge == null) {
            response.getWriter().println("Challenge does not exist.");
        }
        return challenge;
    }    
}